{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Logistic Regression with Differential Privacy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We start by importing the required libraries and modules and collecting the data that we need from the [Adult dataset](https://archive.ics.uci.edu/ml/datasets/adult)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import diffprivlib.models as dp\n",
    "import numpy as np\n",
    "from sklearn.linear_model import LogisticRegression"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train = np.genfromtxt(\"https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data\",\n",
    "                        usecols=(0, 4, 10, 11, 12), delimiter=\",\")\n",
    "\n",
    "y_train = np.genfromtxt(\"https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data\",\n",
    "                        usecols=14, dtype=str, delimiter=\",\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([' <=50K', ' >50K'], dtype='<U6')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.unique(y_train)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's also collect the test data from Adult to test our models once they're trained."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_test = np.genfromtxt(\"https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test\",\n",
    "                        usecols=(0, 4, 10, 11, 12), delimiter=\",\", skip_header=1)\n",
    "\n",
    "y_test = np.genfromtxt(\"https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.test\",\n",
    "                        usecols=14, dtype=str, delimiter=\",\", skip_header=1)\n",
    "\n",
    "# Must trim trailing period \".\" from label\n",
    "y_test = np.array([a[:-1] for a in y_test])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([' <=50K', ' >50K'], dtype='<U6')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.unique(y_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Logistic Regression with no privacy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To begin, let's first train a regular (non-private) logistic regression classifier, and test its accuracy."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>LogisticRegression(max_iter=150)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>LogisticRegression</div></div><div><a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.6/modules/generated/sklearn.linear_model.LogisticRegression.html\">?<span>Documentation for LogisticRegression</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\"><pre>LogisticRegression(max_iter=150)</pre></div> </div></div></div></div>"
      ],
      "text/plain": [
       "LogisticRegression(max_iter=150)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clf = LogisticRegression(solver=\"lbfgs\", max_iter=150)\n",
    "clf.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Non-private test accuracy: 81.28%\n"
     ]
    }
   ],
   "source": [
    "baseline = clf.score(X_test, y_test)\n",
    "print(\"Non-private test accuracy: %.2f%%\" % (baseline * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Differentially private logistic regression"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using the `diffprivlib.models.LogisticRegression` module of diffprivlib, we can train a logistic regression classifier while satisfying differential privacy.\n",
    "\n",
    "If we don't specify any parameters, the model defaults to `epsilon = 1` and `data_norm = None`. If the norm of the data is not specified at initialisation (as in this case), the norm will be calculated on the data when `.fit()` is first called and a warning will be thrown as it causes a privacy leak. To ensure no additional privacy leakage, we should specify the data norm explicitly as an argument, and choose the bounds indepedently of the data (i.e. using domain knowledge).\n",
    "\n",
    "Additionally, the high `data_norm` that is read from the data in this instance gives poor results, with accuracy only slightly better than random. This is as a result of the large amount of noise requires to protect data spread over a large domain. By clipping the data to a smaller domain, accuracy improves markedly, as demonstrated below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "venv3.12/lib/python3.12/site-packages/diffprivlib/models/logistic_regression.py:231: PrivacyLeakWarning: Data norm has not been specified and will be calculated on the data provided.  This will result in additional privacy leakage. To ensure differential privacy and no additional privacy leakage, specify `data_norm` at initialisation.\n",
      "  warnings.warn(\"Data norm has not been specified and will be calculated on the data provided.  This will \"\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-2 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-2 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-2 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-2 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-2 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-2 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-2 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-2 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-2 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-2 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-2 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-2 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-2 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-2 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-2 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-2\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
       "                   data_norm=np.float64(99999.04562544584), random_state=0)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-2\" type=\"checkbox\" checked><label for=\"sk-estimator-id-2\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>LogisticRegression</div></div><div><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\"><pre>LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
       "                   data_norm=np.float64(99999.04562544584), random_state=0)</pre></div> </div></div></div></div>"
      ],
      "text/plain": [
       "LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0)]),\n",
       "                   data_norm=np.float64(99999.04562544584), random_state=0)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dp_clf = dp.LogisticRegression(random_state=0)\n",
    "dp_clf.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Differentially private test accuracy (epsilon=1.00): 79.50%\n"
     ]
    }
   ],
   "source": [
    "print(\"Differentially private test accuracy (epsilon=%.2f): %.2f%%\" % \n",
    "     (dp_clf.epsilon, dp_clf.score(X_test, y_test) * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By setting `epsilon = float(\"inf\")`, we can produce the same result as the non-private logistic regression classifer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-3 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-3 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-3 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-3 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-3 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-3 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-3 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-3 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-3 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-3 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-3 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-3 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-3 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-3 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-3 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-3 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-3 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-3 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-3 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-3\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0), (inf, 0)]),\n",
       "                   data_norm=100000.0, epsilon=inf, max_iter=150)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-3\" type=\"checkbox\" checked><label for=\"sk-estimator-id-3\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>LogisticRegression</div></div><div><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\"><pre>LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0), (inf, 0)]),\n",
       "                   data_norm=100000.0, epsilon=inf, max_iter=150)</pre></div> </div></div></div></div>"
      ],
      "text/plain": [
       "LogisticRegression(accountant=BudgetAccountant(spent_budget=[(1.0, 0), (inf, 0)]),\n",
       "                   data_norm=100000.0, epsilon=inf, max_iter=150)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dp_clf = dp.LogisticRegression(epsilon=float(\"inf\"), data_norm=1e5, max_iter=150)\n",
    "dp_clf.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Agreement between non-private and differentially private (epsilon=inf) classifiers: 100.00%\n"
     ]
    }
   ],
   "source": [
    "print(\"Agreement between non-private and differentially private (epsilon=inf) classifiers: %.2f%%\" % \n",
    "     (dp_clf.score(X_test, clf.predict(X_test)) * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tradeoff of accuracy and privacy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also visualise the tradeoff between accuracy and `epsilon` using `matplotlib`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "from diffprivlib.utils import check_random_state\n",
    "\n",
    "accuracy = []\n",
    "epsilons = np.logspace(-3, 1, 500)\n",
    "random_state = check_random_state(42)\n",
    "n = X_train.shape[0]  # Increased regularisation improves performance in this example\n",
    "\n",
    "for eps in epsilons:\n",
    "    dp_clf = dp.LogisticRegression(epsilon=eps, data_norm=2, random_state=random_state, C=1/n)\n",
    "    dp_clf.fit(X_train, y_train)\n",
    "    accuracy.append(dp_clf.score(X_test, y_test))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's plot the results using `matplotlib`. Because of the norm-clipping applied to the dataset before training (`data_norm=2`), the accuracy plateaus without reaching the non-private baseline."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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6lTY7XaMGzcO6enpeP311zFw4ED8+eefAIDnnnsO6enpOH78OKZNmwYACA8P13xdVq5ciYMHD2LYsGGIj4/Hzp078dFHH2Hnzp34448/THfdh4eHo3fv3li8eDHefvttyf92v/jiCwiCIAYsej755BNcunQJI0eOxNWrV/HOO++gU6dO2L59O+Li4sTj8vLykJKSgnbt2uHNN99U/R99YGAgevfujSVLlmDWrFmS/1UuXboU2dnZ6NevH1AQHM2ZMwf9+/fHgw8+iEuXLuHjjz9GSkoK/vrrLzRq1AixsbH48MMPMWLECPTu3Rt9+vQBADH1unPnTrRt2xaJiYl45plnEBYWhi+//BK9evXCN998g969e5t6Ld127tyJ9u3bIzIyEk899RQCAwMxa9YsdOzYEf/973/RsmVLoCBQuPXWW2Gz2TB+/HiEhYVhzpw5pnptZs+ejcceewz33HMPRo8ejatXr2Lbtm34888/MWDAAPTp0wf79u3DF198gWnTpiEmJgYo+P1Xk5mZifbt22P37t0YPnw4mjRpgrNnz2LZsmU4fvy4eL6W4nxGsrKy0KlTJ5w8eRKjR49GfHw8Fi5cqPpFCQDnzp3DnXfeiX79+uE///kP4uLiTH+md+7cibvuugsNGjTApEmTEBwcjP3792P9+vWmX1stL774Il566SV07twZI0aMwN69e/Hhhx/i77//xvr16yW9nxcuXECXLl3Qp08f9O3bF19//TWefvpp1K9fH3feeafua/3hhx+ibt266NGjBwICAvD999/jkUcegcvlwsiRI8Xj5s+fj+HDh6Nu3boYP348oqOjsXnzZqxYsUJ8HitXrsRdd92FihUriq/97t278cMPP6j+B8mMefPm4erVq3jooYcQHByMcuXKmfqcut1///2YP38+7rzzTjzwwAPIy8vDb7/9hj/++APNmjXDoEGD8OCDD2LHjh2oV6+eeN7ff/+Nffv24fnnny9Su29IAl035s2bJwAQ/v77b81joqKihMaNG4uPJ06cKMjf5g4dOgh169ZVnFulShWhW7dukm2ffvqpYLfbhd9++02yfebMmQIAYf369eI2AILdbhd27twpOfb+++8XKlasKJw9e1ayvV+/fkJUVJRw+fJlQRAEYfXq1QIAoXbt2kJ2drZ43DvvvCMAELZv3y5u69atm1ClShXFczh06JAAQJg3b564zX19T1988YUAQFi7dq24zf36Hjp0SPJadejQQXz8888/CwCEn376SXK9Bg0aSI5T425baGiocPz4cXH7n3/+KQAQxowZI24bMmSIAEB45plnFNcZMmSI5Lm72/T9999LjuvatatQrVo18XFeXp7kdRUEQbhw4YIQFxcnDB8+XNx25swZAYAwceJExb1vu+02oX79+sLVq1fFbS6XS2jTpo1Qo0YN3ecvFPyOeF63V69eQlBQkHDgwAFx27///itEREQIt9xyi7jt0UcfFWw2m7B582Zx27lz54Ry5coZvmc9e/ZU/X339MYbbyiu41alShVhyJAh4uMJEyYIAIQlS5YojnW5XIbPvzifkbfeeksAICxdulQ85sqVK0KtWrUEAMLq1aslrwMAYebMmZJrmv1MT5s2TQAgnDlzRvP5mHlt5Z+r06dPC0FBQcIdd9whOJ1O8bgZM2YIAIS5c+cqnsMnn3wibsvOzhbi4+OFu+++W/e+gsZnPyUlRfK5uHjxohARESG0bNlSuHLliuRY9/uZl5cnVK1aVahSpYpw4cIF1WMEld89N/ln1v1vQWRkpHD69GnJsWY/p6tWrRIACI899pjifu42Xbx4UQgJCRGefvppyf7HHntMCAsLEzIzMxXn+iumyW4w4eHhpkeVmfHVV1+hdu3aqFWrFs6ePSv+6dSpEwAo/kfaoUMHSd2RIAj45ptv0L17dwiCILlGSkoK0tPTsWnTJsk1hg0bJunhaN++PQDg4MGDRXoO7vopALh69SrOnj2LVq1aAYDi3kY6d+6MhIQEfP755+K2HTt2YNu2bfjPf/5j6hq9evVCYmKi+LhFixZo2bIlfvzxR8WxI0aMMLxep06dEBMTg8WLF4vbLly4gJUrV+K+++4TtzkcDvF1dblcOH/+PPLy8tCsWTNTr8P58+exatUq9O3bF5cuXRLfx3PnziElJQX//PMPTpw4Yeo1QEFh7S+//IJevXqhWrVq4vaKFStiwIABWLduHTIyMgAAK1asQOvWrSX/Ky5Xrpypnrjo6GgcP34cf//9t+m26fnmm2/QsGFD1V4wM72MxfmMrFixAomJiejRo4d4fkhIiGpPMArqneQpVLOfaXdd4XfffQeXy6V6/aK8tr/++itycnLw+OOPw24v/Ap68MEHERkZieXLl0uODw8Pl3y2goKC0KJFC1P/Hnh+9tPT03H27Fl06NABBw8eRHp6OlDQ43Pp0iU888wzitot9/u5efNmHDp0CI8//rii3rI4ReF33323ogfS7Of0m2++EQckyLnbFBUVhZ49e4o91yj43C1evBi9evVCWFhYkdt+o2EwdIPJzMxERESE1673zz//YOfOnYiNjZX8ufnmm4GComtPVatWlTw+c+YMLl68iI8++khxDfc/0vJrVK5cWfK4bNmyQMEXfFGcP38eo0ePRlxcHEJDQxEbGyu20/0Poll2ux0DBw7E0qVLcfnyZQDA559/jpCQENx7772mrlGjRg3FtptvvllRUxEQEIBKlSoZXi8gIAB33303vvvuO7HmY8mSJcjNzZUEQwCwYMECNGjQACEhIShfvjxiY2OxfPlyU6/D/v37IQgCXnjhBcV76f4HWf5e6jlz5gwuX76MmjVrKvbVrl0bLpcLx44dAwrqZG666SbFcWrb5J5++mmEh4ejRYsWqFGjBkaOHClJ9Vh14MABScrBquJ8Ro4cOYLq1asrvoC1XofExERFQa7Zz/R9992Htm3b4oEHHkBcXBz69euHL7/8UhIYFeW1PXLkCFBQs+IpKCgI1apVE/e7VapUSfF8y5Yta+rfg/Xr16Nz584ICwtDdHQ0YmNjxZon9+/8gQMHAED3PTVzTFHIfxfczHxODxw4gISEBJQrV073HoMHD8bRo0fx22+/AQXBaFpa2jU9dUppYM3QDeT48eNIT0839QVhlsvlQv369fH222+r7k9KSpI89vyfmPt8APjPf/6DIUOGqF7DcyoAFPzPSE1RZ4Ho27cvfv/9dzz55JNo1KgRwsPD4XK50KVLF83/8eoZPHgw3njjDSxduhT9+/fHwoULcddddyEqKqpI7dMSHBws+Z+znn79+mHWrFn46aef0KtXL3z55ZeoVauWpHjys88+w9ChQ9GrVy88+eSTqFChglj07f7HXo/7tRo3bpxm4aU3f/e8pXbt2ti7dy9++OEHrFixAt988w0++OADTJgwAS+99FKJt8cbn5Gi3gsWPtOhoaFYu3YtVq9ejeXLl2PFihVYvHgxOnXqhF9++QUOh6NEXtui/ntw4MAB3HbbbahVqxbefvttJCUlISgoCD/++COmTZtWpM++EZvNptoup9Operza+1Pcz6lcSkoK4uLi8Nlnn+GWW27BZ599hvj4eHTu3NnytW5kDIZuIJ9++ilQ8MvvLdWrV8fWrVtx2223Fak7ODY2FhEREXA6nV798Jlty4ULF5CamoqXXnoJEyZMELf/888/Rb53vXr10LhxY3z++eeoVKkSjh49ivfee8/0+Wr33rdvX7Fmlb7llltQsWJFLF68GO3atcOqVavw3HPPSY75+uuvUa1aNSxZskTy+sm72bVeW3cqKzAw0CvvZWxsLMqUKYO9e/cq9u3Zswd2u138Yq5SpYpkRKGb2jY1YWFhuO+++3DfffchJycHffr0wauvvorx48cjJCTE0u929erVFaP/isPKZ6RKlSrYtWsXBEGQtNns6wCLn2m73Y7bbrsNt912G95++21MnjwZzz33HFavXi221ei1VXsOALB3715JejQnJweHDh3y2r8T33//PbKzs7Fs2TJJb7M8tV+9enWgIN2tFcx7HqPXvrJly6qm7+S9XXrMfk6rV6+On3/+GefPn9ftHXI4HBgwYADmz5+P1157DUuXLsWDDz7I6Q5kmCa7QaxatQovv/wyqlataqqOwqy+ffvixIkTmD17tmLflStXkJWVpXu+w+HA3XffjW+++Ub1C+TMmTNFaldYWJip1I77Ay//31pxZ68eNGgQfvnlF0yfPh3ly5c3HNXiaenSpZLamr/++gt//vmnpWvI2e123HPPPfj+++/x6aefIi8vT5EiU3st/vzzT2zYsEFynHvUmnyYc4UKFdCxY0fMmjULJ0+eVLTB6nvpcDhwxx134LvvvpOkCNPS0rBw4UK0a9cOkZGRQEGAv2HDBmzZskU87vz585LaLS3yKRSCgoJQp04dCIKA3NxcoOD3Se05q7n77ruxdetWfPvtt4p9Rem9tPIZSUlJwYkTJyRTGVy9elX186nF7Gf6/Pnziv3umi13OtbMayvXuXNnBAUF4d1335W8Xh9//DHS09PRrVs3089Fj9rve3p6OubNmyc57o477kBERASmTJmCq1evSva5z23SpAmqVq2K6dOnK35HPK9fvXp17NmzR/Kebd261VJa1uzn9O6774YgCKo9cPLfw0GDBuHChQt4+OGHkZmZabq+0Z+wZ+g69NNPP2HPnj3Iy8tDWloaVq1ahZUrV6JKlSpYtmyZVydwGzRoEL788kv83//9H1avXo22bdvC6XRiz549+PLLL/Hzzz9LJoBUM3Xq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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.semilogx(epsilons, accuracy, label=\"Differentially private\")\n",
    "plt.plot(epsilons, np.ones_like(epsilons) * baseline, dashes=[2,2], label=\"Non-private\")\n",
    "plt.title(\"Differentially private logistic regression accuracy\")\n",
    "plt.xlabel(\"epsilon\")\n",
    "plt.ylabel(\"Accuracy\")\n",
    "plt.ylim(0, 1)\n",
    "plt.xlim(epsilons[0], epsilons[-1])\n",
    "plt.legend(loc=3)\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
